/*
 * MIT License
 * 
 * Copyright (c) 2017 wen.gu <454727014@qq.com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
 
/***************************************************************************
* Name: android_vfs.c
*
* Purpose: virtual os virtual file sytem interface android implement
*
* Developer:
*   wen.gu , 2016-01-19
*
* TODO:
*
***************************************************************************/


//inner header file
#include "sys_config.h"

#if defined(USE_ANDROID_FILE_INTERFACE) && USE_ANDROID_FILE_INTERFACE
//API header file
#include "sys/vos_vfs.h"

#include "sys/vos_mem.h"
#include "vos_error.h"

#if G_FS_BITS == 64
#define _FILE_OFFSET_BITS 64
#endif

#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#define VOS_LOG_TAG "androidVFS"
//#include "vos_log.h"

//////////////////////////////////////////////////////////////////////////


/***************************************************************************
*
* macro declaration
*
***************************************************************************/
#ifndef O_BINARY
#define O_BINARY 0
#endif

#ifndef LOGV
#define LOGV(...)
#endif

#ifndef LOGE
#define LOGE printf
#endif


/***************************************************************************
*
* data struct declaration
*
***************************************************************************/
typedef struct my_file_handle_s
{
    GS32 fd;
}my_file_handle_t;
/***************************************************************************
*
* API declaration
*
***************************************************************************/

/***************************************************************************
* directory API implement
***************************************************************************/
GPHD vfsOpenDir(const char* dirPath)
{
    GCHECK(dirPath);
    DIR* dir = opendir(dirPath);

    return (GPHD)dir;
}

void vfsCloseDir(GPHD dirHd)
{
    if (dirHd)
    {
        closedir(dirHd);
    }
}

const char* vfsReadFileInDir(GPHD dirHd)
{
    const char* fileName = NULL;
    if (dirHd)
    {
        struct dirent* entry;
        do
        {
            entry = readdir(dirHd);

            if (entry)
            {
                LOGV("fileName(%s), type(%d)\n", entry->d_name, entry->d_type);
                if (entry->d_type == DT_REG)
                {
                    fileName = entry->d_name;
                    break;
                }
            }
            else
            {
                LOGE("readdir failed(%d, %s)\n", errno, strerror(errno));
                break;
            }
        }while(1);
            
    }


    return fileName;
}

/***************************************************************************
* file API implement
***************************************************************************/
static GU32 get_file_open_mode(vfs_open_mode_t mode)
{
    GU32 opMode = 0;
    switch (mode)
    {
    case VFS_MODE_R: opMode = O_RDONLY | O_BINARY;    break;
    case VFS_MODE_RW: opMode = O_RDWR | O_BINARY; break;
    case VFS_MODE_WCA: opMode = O_WRONLY | O_CREAT | O_APPEND | O_BINARY; break;
    case VFS_MODE_WCT: opMode = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY; break;
    case VFS_MODE_RWCA: opMode = O_RDWR | O_CREAT | O_APPEND | O_BINARY; break;
    case VFS_MODE_RWCT: opMode = O_RDWR | O_CREAT | O_TRUNC | O_BINARY; break;
    default:
        LOGE("unsupport this open mode(%d)...\n", mode);
        break;
    }

    return opMode;
}

GPHD vfsFileOpen(const char* name, vfs_open_mode_t mode)
{
    my_file_handle_t* myFd = NULL;
    if (name)
    {
        myFd = (my_file_handle_t*)memAlloc(sizeof(my_file_handle_t));

        if (myFd)
        {
            memset(myFd, 0, sizeof(my_file_handle_t));
            GU32 opMode = get_file_open_mode(mode);
            GS32 fd = open(name, opMode, S_IRWXG | S_IRWXO);

            if (fd >= 0)
            {               
                myFd->fd = fd;
            }
            else
            {
                memFree(myFd);
                myFd = NULL;
                LOGE("Error: open file(%s)failed...err(%d, %s)\n",name, errno, strerror(errno));
            }
        }
        else
        {
            LOGE("alloc file handle failed\n");
        }
    }

    return myFd;
}

void vfsFileClose(GPHD fhd)
{
    if (fhd)
    {
        my_file_handle_t* mfhd = (my_file_handle_t*)fhd;

        close(mfhd->fd);
        memFree(mfhd);
    }
}


GS32 vfsFileRead(GPHD fhd, void* buf, GU32 size)
{
    if (fhd)
    {
        my_file_handle_t* mfhd = (my_file_handle_t*)fhd;

        GS32 ret = read(mfhd->fd, buf, size);

        if (ret >= 0)
        {
            return ret;
        }
        else
        {
            LOGE("read data from file faild,reqSize(%d), err(%d,%s)\n", 
                size, errno, strerror(errno));
        }
    }

    return G_FAIL;
}


GS32 vfsFileWrite(GPHD fhd, const void* buf, GU32 size)
{
    if (fhd)
    {
        my_file_handle_t* mfhd = (my_file_handle_t*)fhd;

        GS32 ret = write(mfhd->fd, buf, size);

        if (ret >= 0)
        {
            return ret;
        }
        else
        {
            LOGE("write data to file faild,reqSize(%d), err(%d,%s)\n", 
                size, errno, strerror(errno));
        }
    }

    return G_FAIL;
}

GS64 vfsFileSeek(GPHD fhd, GS64 offset, vfs_seek_mode_t whence)
{
    if (fhd)
    {
        my_file_handle_t* mfhd = (my_file_handle_t*)fhd;
        GS32 seekMode = -1;
        
        switch (whence)
        {
        case  VFS_SEEK_SET: seekMode = SEEK_SET; break;
        case  VFS_SEEK_CUR: seekMode = SEEK_CUR; break;
        case  VFS_SEEK_END: seekMode = SEEK_END; break;
        default:
            LOGE("unknown this seek mode(%d)\n", whence);
            break;
        }

        if (seekMode >= 0)
        {
            off_t ret = lseek(mfhd->fd, offset, seekMode);

            if (ret >= 0)
            {
                return (GS64)ret;
            }
            else
            {
                LOGE("do file seek fialed,err(%d, %s),mode(%d), offset(%lld)\n",
                    errno, strerror(errno), seekMode, offset);
            }
        }
    }

    return G_FAIL;
}


GS64 vfsFileTell(GPHD fhd)
{
    if (fhd)
    {
        my_file_handle_t* mfhd = (my_file_handle_t*)fhd;

        off_t ret = lseek(mfhd->fd, 0, SEEK_CUR);

        if (ret >= 0)
        {
            return (GS64)ret;
        }
        else
        {
            LOGE("tell current file write/read position failed, err(%d,%s)\n", 
                errno, strerror(errno));
        }
    }

    return G_FAIL;
}


GS32 vfsFileSync(GPHD fhd)
{
    if (fhd)
    {
        my_file_handle_t* mfhd = (my_file_handle_t*)fhd;

        GS32 ret = fsync(mfhd->fd);

        if (ret >= 0)
        {
            return ret;
        }
        else
        {
            LOGE("do file sync failed, err(%d,%s)\n", 
                errno, strerror(errno));
        }
    }

    return G_FAIL;
}


GS64 vfsFileSize(GPHD fhd)
{
    if (fhd)
    {
        my_file_handle_t* mfhd = (my_file_handle_t*)fhd;
        struct stat fileStat = {0};

        GS32 ret = fstat(mfhd->fd, &fileStat);

        if (ret == 0)
        {
            return (GS64)(fileStat.st_size);
        }
        else
        {
            LOGE("get file size with fsate failed,err(%d, %s)\n", errno, strerror(errno));
        }
    }

    return 0;
}

#endif //end of USE_LINUX_FILE_INTERFACE